
/**
 * 函数编排
 * 执行器
 * 函数执行任务流数据
 * 完整的日志功能
 * 调试模式下会输出日志到控制台
 * 可以配置日志格式
 * config {dos:调用链 , 模式 ，链路 id ,上下文 ctx ,}
 * {name,args,ctx,before,after}
 * @param name string 函数名 格式 包名::函数名  | 函数名 => window.函数名  
 * @param args 自定义参数集合， 其中 字符串 $ 代表 上一个函数的返回值或者初始值 会进行符号代换 [$,a,b,c] 默认 [$]
 * @param ctx 局部上下文，当前函数执行的上下文
 * @param before 钩子函数 定义：（...args） => new Args  before 可以是 一个新的函数链路 
 * @param after 狗子函数 定义: (res) => new Res  after 也可以是一个新的函数链路
 * @param id 外部 id 可以作为链路 id 一直传递下去
 * @param 
 * // 如果想保持原样输入 可以在 符号前加 \ \@{name}  => @{name}
 *  @{name}  => 调用链名称
 *  #{name} => 参数替换
 *  ${name} => 变量替换
 *  &{name} => 上下文参数
 * 
 */


class Do {
    constructor(config) { }

    // 
    fnMap = new Map()
    id = Math.random().toString(36).split('.').pop();
    mode = 'prod'; // prod,dev,debug
    //
    logFormat = function () { return `${id} ${date} ${status} ${name} ${title} ${desc}`; }
    logs = [];
    // 函数链
    fnList = []
    // 生成日志
    newLog(name, title, desc, args, status = 'info') {
        const ctx = { id: this.id, date: new Date().toLocaleString(), status, title, desc, name }
        const log = this.logFormat.call(ctx)
        this.logs.push(log);
        if (this.mode == 'debug' || status == 'error') {
            switch (status) {
                case "info": console.info(log); break;
                case "warn": console.warn(log); break;
                case "error": console.error(log); break;
                case "debug": console.debug(log); break;
                default: console.log(log)
            }
            console.log("args ====> ", args)
        }
    }
    // 获取日志
    getLog() { }
    //
    // 通过名称获取完整的 调用链   调用链格式设定
    getList(name){}
    /**
     * @param item 
     * @param md 上一个函数的运行返回值
    */
    async do(item, ...mds) {
        let { name, args = [], ctx, before, after } = item;
        //
        let argss = [...mds, ...args]
        // 钩子函数
        if (before) {
            if (Array.isArray(before)) {
                argss = await this.doList(before, argss)
            }
            else if (typeof before === 'object') {
                argss = await this.do(before, ...argss)
            }
        }
        //
        const ns = name.split('::');
        let fn = undefined
        let res = undefined;
        if (ns.length == 1) {
            fn = window[name]
        } else {
            if (ctx == undefined) {
                ctx = this.fnMap.get(ns[0])
            }
            fn = this.fnMap.get(ns[0])?.[ns[1]]
        }
        //
        if (fn != undefined && typeof fn == 'function') {
            //
            this.newLog(name, '准备执行', '', argss)
            //
            try {
                res = fn.apply(ctx, argss)
                if (res && typeof res.then == 'function') {
                    res = await res;
                }
                this.newLog(name, '执行完成', '', argss)

            } catch (error) {
                this.newLog(name,'执行失败',error,argss,'error')
                throw new Error(name + ' ===> 执行异常终止')
            }

            if(after){
                if(Array.isArray(after)){
                    res = await this.doList(after,[res,...argss])
                }else{
                    res = await this.do(after,res)
                }
            }

            return res;
            
        } else {
            this.newLog(name, '函数获取失败','',argss,'error')
            throw new Error(name + ' ===> 异常终止')
        }
    }

    // 函数链
    async doList(list, args) {

    }
    // 调用链名称
    async doLink(name,args){
        
    }
}